package com.ihr360.job.core.repository.dao;

import com.ihr360.job.core.repository.ExecutionContextSerializer;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.serializer.Deserializer;
import org.springframework.core.serializer.Serializer;
import org.springframework.util.Assert;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Map;

/**
 * Implementation that uses XStream and Jettison to provide serialization.
 *
 * @author Thomas Risberg
 * @author Michael Minella
 * @since 2.0
 * @see ExecutionContextSerializer
 */
public class XStreamExecutionContextStringSerializer implements ExecutionContextSerializer, InitializingBean {

    private ReflectionProvider reflectionProvider = null;

    private HierarchicalStreamDriver hierarchicalStreamDriver;

    private XStream xstream;

    public void setReflectionProvider(ReflectionProvider reflectionProvider) {
        this.reflectionProvider = reflectionProvider;
    }

    public void setHierarchicalStreamDriver(HierarchicalStreamDriver hierarchicalStreamDriver) {
        this.hierarchicalStreamDriver = hierarchicalStreamDriver;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        init();
    }

    public synchronized void init() throws Exception {
        if (hierarchicalStreamDriver == null) {
            this.hierarchicalStreamDriver = new JettisonMappedXmlDriver();
        }
        if (reflectionProvider == null) {
            xstream =  new XStream(hierarchicalStreamDriver);
        }
        else {
            xstream = new XStream(reflectionProvider, hierarchicalStreamDriver);
        }
    }

    /**
     * Serializes the passed execution context to the supplied OutputStream.
     *
     * @param context
     * @param out
     * @see Serializer#serialize(Object, OutputStream)
     */
    @Override
    public void serialize(Map<String, Object> context, OutputStream out) throws IOException {
        Assert.notNull(context);
        Assert.notNull(out);

        out.write(xstream.toXML(context).getBytes());
    }

    /**
     * Deserializes the supplied input stream into a new execution context.
     *
     * @param in
     * @return a reconstructed execution context
     * @see Deserializer#deserialize(InputStream)
     */
    @SuppressWarnings("unchecked")
    @Override
    public Map<String, Object> deserialize(InputStream in) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(in));

        StringBuilder sb = new StringBuilder();

        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }

        return (Map<String, Object>) xstream.fromXML(sb.toString());
    }
}
